package com.yl.request.common.glaencrypt;

import com.yl.request.common.glaencrypt.enums.CryptLevel;
import com.yl.request.common.glaencrypt.sm2.SM2Utils;
import org.bouncycastle.crypto.io.InvalidCipherTextIOException;
import org.bouncycastle.util.encoders.Base64;

import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.io.*;
import java.security.*;

public class DxpDecryptInputStream extends FilterInputStream {
    private final int DXP_FILE_HEAD_SIZE = 137;
    private final int DXP_BLOCK_SIZE = 16;
    private final int DXP_CHUNK_SIZE = 65536;
    private final byte[] inputBuffer = new byte[65552];
    private boolean finalized = false;
    private byte[] buf;
    private int maxBuf;
    private int bufOff;
    private boolean headHandled = false;
    private long cryptLength;
    private Cipher cipher;
    private final CryptLevel cryptLevel;
    private final String sm2Privatekey;

    protected DxpDecryptInputStream(InputStream input, CryptLevel cryptLevel, String sm2Privatekey) {
        super(input);
        this.cryptLevel = cryptLevel;
        this.sm2Privatekey = sm2Privatekey;
    }

    private int nextChunk() throws IOException {
        if (this.finalized) {
            return -1;
        } else {
            if (!this.headHandled) {
                this.cryptLength = this.parseFileHead();
                this.headHandled = true;
            }

            this.bufOff = 0;
            this.maxBuf = 0;
            int off = 0;
            int len = 65552;
            int size = 0;

            while (true) {
                int read;
                do {
                    if (this.maxBuf != 0) {
                        return this.maxBuf;
                    }

                    read = this.in.read(this.inputBuffer, off, len);
                    if (read != -1) {
                        off += read;
                        len -= read;
                        size += read;
                    }
                } while (read != -1 && size != 65552);

                if (read == -1 && size == 0) {
                    this.buf = this.finaliseCipher();
                    if (this.buf != null && this.buf.length != 0) {
                        this.maxBuf = this.buf.length;
                        return this.maxBuf;
                    }

                    return -1;
                }

                try {
                    this.buf = this.cipher.doFinal(this.inputBuffer, 0, size);
                } catch (GeneralSecurityException var6) {
                    var6.printStackTrace();
                    throw new InvalidCipherTextIOException("Error cipher", var6);
                }

                if (this.buf != null) {
                    this.maxBuf = this.buf.length;
                }
            }
        }
    }

    private byte[] finaliseCipher() throws InvalidCipherTextIOException {
        if (!this.finalized) {
            this.finalized = true;
        }

        return null;
    }

    @Override
    public int read() throws IOException {
        return this.bufOff >= this.maxBuf && this.nextChunk() < 0 ? -1 : this.buf[this.bufOff++] & 255;
    }

    @Override
    public int read(byte[] b, int off, int len) throws IOException {
        if (this.bufOff >= this.maxBuf && this.nextChunk() < 0) {
            return -1;
        } else {
            int toSupply = Math.min(len, this.available());
            System.arraycopy(this.buf, this.bufOff, b, off, toSupply);
            this.bufOff += toSupply;
            return toSupply;
        }
    }

    @Override
    public long skip(long n) throws IOException {
        if (n <= 0L) {
            return 0L;
        } else {
            int skip = (int) Math.min(n, this.available());
            this.bufOff += skip;
            return skip;
        }
    }

    @Override
    public int available() throws IOException {
        return this.maxBuf - this.bufOff;
    }

    @Override
    public void close() throws IOException {
        try {
            this.in.close();
        } finally {
            if (!this.finalized) {
                this.finaliseCipher();
            }

        }

        this.maxBuf = this.bufOff = 0;
    }

    @Override
    public void mark(int readlimit) {
    }

    @Override
    public void reset() throws IOException {
    }

    @Override
    public boolean markSupported() {
        return false;
    }

    public long getCryptLength() throws IOException {
        if (!this.headHandled) {
            this.cryptLength = this.parseFileHead();
            this.headHandled = true;
        }

        return this.cryptLength;
    }

    private long parseFileHead() throws IOException {
        byte[] headBytes = new byte[137];
        int off = 0;
        int len = 137;
        int size = 0;

        do {
            int read = this.in.read(headBytes, off, len);
            if (read == -1) {
                break;
            }

            off += read;
            len -= read;
            size += read;
        } while (size != 137);

        byte[] ilevelBytes = this.cryptLevel.Level().getBytes();
        byte[] levelBytes = new byte[1];
        System.arraycopy(headBytes, 8, levelBytes, 0, 1);
        if (ilevelBytes[0] < levelBytes[0]) {
            throw new IOException("Crypt level mismatch");
        } else {
            byte[] sizeBytes = new byte[12];
            System.arraycopy(headBytes, 12, sizeBytes, 0, 12);
            long cryptLength = Long.valueOf(new String(sizeBytes));

            try {
                byte[] encryptKeyBytes = new byte[113];
                System.arraycopy(headBytes, 24, encryptKeyBytes, 0, 113);
                PrivateKey pri = SM2Utils.generatePrivateKey(Base64.decode(this.sm2Privatekey));
                byte[] keyBytes = SM2Utils.decrypt(pri, encryptKeyBytes);
                this.cipher = Cipher.getInstance("SM4/ECB/PKCS7Padding", "BC");
                Key sm4Key = new SecretKeySpec(keyBytes, "SM4");
                this.cipher.init(2, sm4Key);
                return cryptLength;
            } catch (Exception var14) {
                var14.printStackTrace();
                throw new InvalidCipherTextIOException("Parse file head error", var14);
            }
        }
    }
}
